<?php

namespace App\Middleware\Api;

use _HumbugBoxcb6a53192cfd\Nette\Neon\Exception;
use App\Exception\XZXValidationException;
use App\Model\Dao\MchAccountDao;
use App\Model\Dao\MchConfigDao;
use App\Model\Dao\MchInfoDao;
use App\Model\Dao\MchTradeWebsiteDao;
use App\Service\MchAccountStatusService;
use App\Service\MchConfigStatusService;
use App\Service\MchInfoStatusService;
use App\Utils\CustomUtils;
use App\Utils\SignUtils;
use Hyperf\Di\Annotation\Inject;
use Hyperf\Utils\Arr;
use Hyperf\Validation\Contract\ValidatorFactoryInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Psr\Http\Message\ResponseInterface;
use Hyperf\Utils\Context;

class PayApiAuthMiddleware implements MiddlewareInterface {

    /**
     * @Inject()
     * @var ValidatorFactoryInterface
     */
    protected $validationFactory;

    /**
     * @var MchInfoDao
     * @Inject()
     */
    protected $MchInfoDao;

    /**
     * @var MchConfigDao
     * @Inject()
     */
    protected $MchConfigDao;

    /**
     * @var MchAccountDao
     * @Inject()
     */
    protected $MchAccountDao;

    /**
     * @var MchTradeWebsiteDao
     * @Inject()
     */
    protected $MchTradeWebsiteDao;

    /**
     * @var MchInfoStatusService
     * @Inject()
     */
    protected $MchInfoStatusService;

    /**
     * @var MchConfigStatusService
     * @Inject()
     */
    protected $MchConfigStatusService;

    /**
     * @var MchAccountStatusService
     * @Inject()
     */
    protected $MchAccountStatusService;

    /**
     * @param ServerRequestInterface  $request
     * @param RequestHandlerInterface $handler
     *
     * @return ResponseInterface
     * @throws XZXValidationException
     * @throws \Exception
     */
    public function process (ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface {

        $parseBody = $request->getParsedBody();

        $this->_validOrFail($parseBody, [
            'appId'  => 'required',
            'mchUrl' => 'required',
        ]);

        $appId  = $parseBody['appId'];
        $mchUrl = $parseBody['mchUrl'];

        $mchInfo = $this->MchInfoDao->getWhere(['appId' => $appId]);

        // 可以用API吗?
        $this->MchInfoStatusService->verifyApiAvailable($mchInfo);

        // 交易网站有效吗?
        $this->_verifyMchUrl($mchInfo['mchNo'], $mchUrl);

        // 获取商户账户信息并检查可用状态
        $mchAccount = $this->MchAccountDao->getWhere(['mchNo' => $mchInfo['mchNo']]);
        $this->MchAccountStatusService->verifyAvailable($mchAccount);

        // 获取商户配置
        $mchConfig = $this->MchConfigDao->getWhere(['mchNo' => $mchInfo['mchNo']]);
        $this->MchConfigStatusService->verifyAvailable($mchConfig);

        // 追加参数
        $ext = array_merge($request->getAttribute("ext", []), [
            'mchId'      => $mchInfo['id'],
            'mchNo'      => $mchInfo['mchNo'],

            'mchInfo'    => $mchInfo,
            'mchConfig'  => $mchConfig,
            'mchAccount' => $mchAccount,
            'mchUrl'     => $mchUrl,
        ]);

        // 全局修改$request，后面控制器需要接受ext
        $request = $request->withAttribute("ext", $ext);
        $request = Context::set(ServerRequestInterface::class, $request);

        return $handler->handle($request);
    }

    /**
     * 表单校验抛异常封装
     *
     * @param       $params
     * @param array $rules
     * @param array $messages
     * @param array $customAttrs
     *
     * @throws \Exception
     */
    protected function _validOrFail ($params, $rules = [], $messages = [], $customAttrs = []) {

        $validator = $this->validationFactory->make($params, $rules, $messages, $customAttrs);

        if ($validator->fails()) {
            $errorMessage = $validator->errors()->first();
            throw new \Exception($errorMessage);
        }
    }

    /**
     * 检查商户交易网站
     *
     * @param string $mchNo  商户号
     * @param string $mchUrl 交易网站
     */
    protected function _verifyMchUrl ($mchNo, $mchUrl) {

        $info = $this->MchTradeWebsiteDao->getWhere([
            'mchNo' => $mchNo,
            'url'   => $mchUrl,
        ]);

        if (!$info) {
            throw new XZXValidationException(trans('pay.mch_url.not_found'));
        }

        if ($info['status'] != 1) {
            throw new XZXValidationException(trans('pay.mch_url.not_available'));
        }

        if ($info['reviewStatus'] != 1) {
            throw new XZXValidationException(trans('pay.mch_url.not_review'));
        }

    }
}
